//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<enumeration10.cpp>
///	@path	~/src/graphics/dx9/
///	@date	2007/09/26
///	@desc	Enumerates available Direct3D9 adapters, devices, modes, etc.

#include "config/config.h"
#include "config/os.h"

#include "graphics/DxHelpers.h"
#include "graphics/dx9/enumeration9.h"
#include "graphics/dx9/CDevice9.h"

#include <algorithm>
#include <strsafe.h>

TRACE_CATEGORY( _S("Graphics/DX9/enum") , TRACE_LV_INFO );

namespace xeres {

	// get instance of EnumD3D9
	EnumD3D9& EnumD3D9::instance( void )
	{
		static EnumD3D9 s_EnumD3D9_instance;
		return s_EnumD3D9_instance;
	}

	// ctor
	EnumD3D9::EnumD3D9( void )
		: m_inited( false )
		, m_minWidth( 640 )
		, m_maxWidth( UINT_MAX )
		, m_minHeight( 480 )
		, m_maxHeight( UINT_MAX )
		, m_refreshMin( 0 )
		, m_refreshMax( UINT_MAX )
		, m_multisampleQualityMax( 0xFFFF )
	{
		ResetPossibleDepthStencilFormats();
		ResetPossibleMultisampleTypeList();
		ResetPossiblePresentIntervalList();
		SetPossibleVertexProcessingList( true, true, true, false );
	}

	// dtor
	EnumD3D9::~EnumD3D9( void )
	{
		if( !m_inited )
			Finalize();
	}

	void EnumD3D9::Initialize( bool bEnumerateAllAdapterFormats )
	{
		TRACK_FUNCTION_SCOPE();

		m_enumerateAllAdapterFormats = bEnumerateAllAdapterFormats;
		Enumerate();
	}

	// finalize
	void EnumD3D9::Finalize( void )
	{
		ClearAdapterInfoList();
		m_inited = false;
	}

	static int __cdecl SortModesCallback( const void* arg1, const void* arg2 )
	{
		D3DDISPLAYMODE* pdm1 = (D3DDISPLAYMODE*)(arg1);
		D3DDISPLAYMODE* pdm2 = (D3DDISPLAYMODE*)(arg2);

		if (pdm1->Width > pdm2->Width)
			return 1;
		if (pdm1->Width < pdm2->Width)
			return -1;
		if (pdm1->Height > pdm2->Height)
			return 1;
		if (pdm1->Height < pdm2->Height)
			return -1;
		if (pdm1->Format > pdm2->Format)
			return 1;
		if (pdm1->Format < pdm2->Format)
			return -1;
		if (pdm1->RefreshRate > pdm2->RefreshRate)
			return 1;
		if (pdm1->RefreshRate < pdm2->RefreshRate)
			return -1;
		return 0;
	}

	//--------------------------------------------------------------------------------------
	// Enumerate for each adapter all of the supported display modes, 
	// device types, adapter formats, back buffer formats, window/full screen support, 
	// depth stencil formats, multi-sampling types/qualities, and presentations intervals.
	//
	// For each combination of device type (HAL/REF), adapter format, back buffer format, and
	// IsWindowed it will call the app's ConfirmDevice callback.  This allows the app
	// to reject or allow that combination based on its caps/etc.  It also allows the 
	// app to change the BehaviorFlags.  The BehaviorFlags defaults non-pure HWVP 
	// if supported otherwise it will default to SWVP, however the app can change this 
	// through the ConfirmDevice callback.
	//--------------------------------------------------------------------------------------
	void EnumD3D9::Enumerate( void )
	{
		TRACK_FUNCTION_SCOPE();

		TRACE_INFO( _S("Enumerate all available Device settings...") );

		IDirect3D9* pD3D = CDevice9::d3d9();

		m_inited = true; 

		ClearAdapterInfoList();
		std::vector<D3DFORMAT> adapterFormatList;

		const D3DFORMAT allowedAdapterFormatArray[] = 
		{   
			D3DFMT_X8R8G8B8, 

			// FIXME! should this legacy format be available?
			D3DFMT_X1R5G5B5, 
			D3DFMT_R5G6B5, 
			D3DFMT_A2R10G10B10
		};

		const UINT allowedAdapterFormatArrayCount  = sizeof(allowedAdapterFormatArray) / sizeof(allowedAdapterFormatArray[0]);

		UINT numAdapters = pD3D->GetAdapterCount();

		TRACE_DEBUG( _S("Found %i DirectX 9 adapters.") , numAdapters );

		for (UINT adapterOrdinal = 0; adapterOrdinal < numAdapters; adapterOrdinal++)
		{
			EnumAdapterInfo9* pAdapterInfo = new EnumAdapterInfo9;
			pAdapterInfo->m_adapterOrdinal = adapterOrdinal;
			pD3D->GetAdapterIdentifier(adapterOrdinal, 0, &pAdapterInfo->m_adapterIdentifier);

			TRACE_DEBUG( _S("Examining adapter '%S' in driver '%S' of '%S'.") ,
				pAdapterInfo->m_adapterIdentifier.DeviceName ,
				pAdapterInfo->m_adapterIdentifier.Driver ,
				pAdapterInfo->m_adapterIdentifier.Description );

			// Get list of all display modes on this adapter.  
			// Also build a temporary list of all display adapter formats.
			adapterFormatList.clear();

			for( UINT iFormatList = 0; iFormatList < allowedAdapterFormatArrayCount; iFormatList++ )
			{
				D3DFORMAT allowedAdapterFormat = allowedAdapterFormatArray[iFormatList];
				UINT numAdapterModes = pD3D->GetAdapterModeCount( adapterOrdinal, allowedAdapterFormat );
				for (UINT mode = 0; mode < numAdapterModes; mode++)
				{
					D3DDISPLAYMODE displayMode;
					pD3D->EnumAdapterModes( adapterOrdinal, allowedAdapterFormat, mode, &displayMode );

					if( displayMode.Width < m_minWidth ||
						displayMode.Height < m_minHeight || 
						displayMode.Width > m_maxWidth ||
						displayMode.Height > m_maxHeight || 
						displayMode.RefreshRate < m_refreshMin ||
						displayMode.RefreshRate > m_refreshMax )
					{
						continue;
					}

					TRACE_DEBUG( _S("Found valid display mode %i X %i %s") ,
							displayMode.Width , displayMode.Height , d3d_format_to_string( displayMode.Format ).c_str() );

					pAdapterInfo->m_displayModeList.push_back( displayMode );


					if( std::find( adapterFormatList.begin() , adapterFormatList.end() , displayMode.Format )
						== adapterFormatList.end() )
					{
						TRACE_DEBUG( _S("Found valid format %s.") , d3d_format_to_string( displayMode.Format ).c_str() );

						adapterFormatList.push_back( displayMode.Format );
					}
				}
			}

			D3DDISPLAYMODE displayMode;
			pD3D->GetAdapterDisplayMode( adapterOrdinal, &displayMode );
			if( std::find( adapterFormatList.begin() , adapterFormatList.end() , displayMode.Format )
				== adapterFormatList.end() )
			{
				adapterFormatList.push_back( displayMode.Format );
			}

			// Sort display mode list

			// create temporary list for sort
			size_t size = pAdapterInfo->m_displayModeList.size();
			D3DDISPLAYMODE * modes = static_cast<D3DDISPLAYMODE*>( alloca( sizeof(D3DDISPLAYMODE) * size ) );
			for( size_t i = 0 ; i < size ; ++i )
				modes[i] = pAdapterInfo->m_displayModeList[i];
			qsort( modes , size , sizeof( D3DDISPLAYMODE ) , SortModesCallback );
			for( size_t i = 0 ; i < size ; ++i )
				pAdapterInfo->m_displayModeList[i] = modes[i];

			// Get info for each device on this adapter
			if( FAILED( EnumerateDevices( pAdapterInfo, &adapterFormatList ) ) )
			{
				delete pAdapterInfo;
				continue;
			}

			// If at least one device on this adapter is available and compatible
			// with the app, add the adapterInfo to the list
			if( pAdapterInfo->m_deviceInfoList.size() > 0 )
				m_adapterInfoList.push_back( pAdapterInfo );
			else
				delete pAdapterInfo;
		}

		//
		// Check for 2 or more adapters with the same name. Append the name
		// with some instance number if that's the case to help distinguish
		// them.
		//
		bool bUniqueDesc = true;
		EnumAdapterInfo9* pAdapterInfo;
		for( int i = 0 ; i < (int)m_adapterInfoList.size() ; ++i )
		{
			EnumAdapterInfo9* pAdapterInfo1 = m_adapterInfoList[i];

			for( int j = i + 1 ; j < (int)m_adapterInfoList.size() ; ++j )
			{
				EnumAdapterInfo9* pAdapterInfo2 = m_adapterInfoList[j];
				if( _stricmp( pAdapterInfo1->m_adapterIdentifier.Description, 
					pAdapterInfo2->m_adapterIdentifier.Description ) == 0 )
				{
					bUniqueDesc = false;
					break;
				}
			}

			if( !bUniqueDesc )
				break;
		}

		for( int i = 0 ; i < (int)m_adapterInfoList.size() ; ++i )
		{
			pAdapterInfo = m_adapterInfoList[i];

			::MultiByteToWideChar( CP_ACP, 0, 
				pAdapterInfo->m_adapterIdentifier.Description, -1, 
				pAdapterInfo->m_uniqueDescription, 100 );
			pAdapterInfo->m_uniqueDescription[100] = 0;

			if( !bUniqueDesc )
			{
				WCHAR sz[100];
				StringCchPrintf( sz, 100, _S(" (#%d)"), pAdapterInfo->m_adapterOrdinal );
				StringCchCat( pAdapterInfo->m_uniqueDescription, 256, sz );

			}
		}
	}

	//--------------------------------------------------------------------------------------
	// Enumerates D3D devices for a particular adapter.
	//--------------------------------------------------------------------------------------
	HRESULT EnumD3D9::EnumerateDevices( EnumAdapterInfo9* pAdapterInfo, std::vector<D3DFORMAT>* pAdapterFormatList )
	{
		TRACK_FUNCTION_SCOPE();

		TRACE_DEBUG( _S("Getting info from adapter '%S'.") , pAdapterInfo->m_adapterIdentifier.Description );

		HRESULT hr;

		const D3DDEVTYPE devTypeArray[] = 
		{ 
			D3DDEVTYPE_HAL, 
			D3DDEVTYPE_SW, 
			D3DDEVTYPE_REF 
		};
		const UINT devTypeArrayCount = sizeof(devTypeArray) / sizeof(devTypeArray[0]);

		// Enumerate each Direct3D device type
		for( UINT iDeviceType = 0; iDeviceType < devTypeArrayCount; iDeviceType++ )
		{
			EnumDeviceInfo9* pDeviceInfo = new EnumDeviceInfo9;
			if( pDeviceInfo == NULL )
				return E_OUTOFMEMORY;

			// Fill struct w/ AdapterOrdinal and D3DDEVTYPE
			pDeviceInfo->m_adapterOrdinal = pAdapterInfo->m_adapterOrdinal;
			pDeviceInfo->m_deviceType = devTypeArray[iDeviceType];

			// Store device caps
			if( FAILED( hr = CDevice9::d3d9()->GetDeviceCaps( pAdapterInfo->m_adapterOrdinal, pDeviceInfo->m_deviceType, 
				&pDeviceInfo->m_caps ) ) )
			{
				delete pDeviceInfo;
				continue;
			}

			if( pDeviceInfo->m_deviceType != D3DDEVTYPE_HAL )
			{
				// don't try ref device!
				if( !m_enumerateAllAdapterFormats )
					continue;

				IDirect3D9* pD3D = CDevice9::d3d9();

				// Create a temp device to verify that it is really possible to create a REF device 
				// [the developer DirectX redist has to be installed]

				HWND hwnd = ::GetDesktopWindow();

				D3DDISPLAYMODE Mode;
				pD3D->GetAdapterDisplayMode(0, &Mode);
				D3DPRESENT_PARAMETERS pp;
				ZeroMemory( &pp, sizeof(D3DPRESENT_PARAMETERS) );
				pp.BackBufferWidth  = 1;
				pp.BackBufferHeight = 1;
				pp.BackBufferFormat = Mode.Format;
				pp.BackBufferCount  = 1;
				pp.SwapEffect       = D3DSWAPEFFECT_COPY;
				pp.Windowed         = TRUE;
				pp.hDeviceWindow    = hwnd;

				IDirect3DDevice9 *pDevice = NULL;
				if( FAILED( hr = pD3D->CreateDevice( pAdapterInfo->m_adapterOrdinal, pDeviceInfo->m_deviceType , hwnd ,
					D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, &pDevice ) ) )
				{
					TRACE_DEBUG( _S("Invalid mode '%s' found in '%S'.") , 
						d3d_devtype_to_string( pDeviceInfo->m_deviceType ).c_str() ,
						pAdapterInfo->m_adapterIdentifier.Description );
					delete pDeviceInfo;
					continue;
				}
				pDevice->Release();
			}

			// Get info for each devicecombo on this device
			if( FAILED( hr = EnumerateDeviceSettings( pAdapterInfo, pDeviceInfo, pAdapterFormatList ) ) )
			{
				delete pDeviceInfo;
				continue;
			}

			// If at least one devicecombo for this device is found, 
			// add the deviceInfo to the list
			if (pDeviceInfo->m_deviceSettingsList.size() > 0 )
				pAdapterInfo->m_deviceInfoList.push_back( pDeviceInfo );
			else
				delete pDeviceInfo;
		}

		return S_OK;
	}



	//--------------------------------------------------------------------------------------
	// Enumerates DeviceCombos for a particular device.
	//--------------------------------------------------------------------------------------
	HRESULT EnumD3D9::EnumerateDeviceSettings( EnumAdapterInfo9* pAdapterInfo, EnumDeviceInfo9* pDeviceInfo, std::vector<D3DFORMAT>* pAdapterFormatList )
	{
		const D3DFORMAT backBufferFormatArray[] = 
		{   
			D3DFMT_A8R8G8B8, 
			D3DFMT_X8R8G8B8, 
			D3DFMT_A2R10G10B10, 
			D3DFMT_R5G6B5, 
			D3DFMT_A1R5G5B5, 
			D3DFMT_X1R5G5B5 
		};
		const UINT backBufferFormatArrayCount = sizeof(backBufferFormatArray) / sizeof(backBufferFormatArray[0]);

		// See which adapter formats are supported by this device
		for( int iFormat = 0 ; iFormat < (int)pAdapterFormatList->size() ; ++iFormat )
		{
			D3DFORMAT adapterFormat = pAdapterFormatList->at(iFormat);

			for( UINT iBackBufferFormat = 0 ; iBackBufferFormat < backBufferFormatArrayCount ; iBackBufferFormat++ )
			{
				D3DFORMAT backBufferFormat = backBufferFormatArray[iBackBufferFormat];

				for( int nWindowed = 0; nWindowed < 2; nWindowed++)
				{
					if( !nWindowed && pAdapterInfo->m_displayModeList.size() == 0 )
						continue;

					if( FAILED( CDevice9::d3d9()->CheckDeviceType( pAdapterInfo->m_adapterOrdinal, pDeviceInfo->m_deviceType,
						adapterFormat, backBufferFormat, nWindowed )))
					{
						continue;
					}

					if( m_requirePostPixelShaderBlending )
					{
						// If the backbuffer format doesn't support D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING
						// then alpha test, pixel fog, render-target blending, color write enable, and dithering. 
						// are not supported.
						if( FAILED( CDevice9::d3d9()->CheckDeviceFormat( pAdapterInfo->m_adapterOrdinal, pDeviceInfo->m_deviceType,
							adapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, 
							D3DRTYPE_TEXTURE, backBufferFormat ) ) )
						{
							continue;
						}
					}

					// If an application callback function has been provided, make sure this device
					// is acceptable to the app.
//					if( m_IsD3D9DeviceAcceptableFunc != NULL )
//					{
//						if( !m_IsD3D9DeviceAcceptableFunc( &pDeviceInfo->m_aps, adapterFormat, backBufferFormat, FALSE != nWindowed, m_pIsD3D9DeviceAcceptableFuncUserContext ) )
//							continue;
//					}

					// At this point, we have an adapter/device/adapterformat/backbufferformat/iswindowed
					// DeviceCombo that is supported by the system and acceptable to the app. We still 
					// need to find one or more suitable depth/stencil buffer format,
					// multisample type, and present interval.
					EnumDeviceSettings9* pDeviceSettings = new EnumDeviceSettings9;
					if( pDeviceSettings == NULL )
						return E_OUTOFMEMORY;

					pDeviceSettings->m_adapterOrdinal = pAdapterInfo->m_adapterOrdinal;
					pDeviceSettings->m_deviceType = pDeviceInfo->m_deviceType;
					pDeviceSettings->m_adapterFormat = adapterFormat;
					pDeviceSettings->m_backbufferFormat = backBufferFormat;
					pDeviceSettings->m_windowed = (nWindowed != 0);

					BuildDepthStencilFormatList( pDeviceSettings );
					BuildMultiSampleTypeList( pDeviceSettings );
					if (pDeviceSettings->m_multiSampleTypeList.size() == 0)
					{
						delete pDeviceSettings;
						continue;
					}
					BuildDSMSConflictList( pDeviceSettings );
					BuildPresentIntervalList(pDeviceInfo, pDeviceSettings );
					pDeviceSettings->m_adapterInfo = pAdapterInfo;
					pDeviceSettings->m_deviceInfo = pDeviceInfo;

					TRACE_DEBUG( _S("Found valid device settings %s %s %s in %S") ,
							nWindowed ? _S("windowed") : _S("fullscreen") ,
							d3d_format_to_string( adapterFormat ).c_str() ,
							d3d_devtype_to_string( pDeviceInfo->m_deviceType ).c_str() ,
							pAdapterInfo->m_adapterIdentifier.Description );

					pDeviceInfo->m_deviceSettingsList.push_back( pDeviceSettings );
				}
			}
		}

		return S_OK;
	}

	//--------------------------------------------------------------------------------------
	// Adds all depth/stencil formats that are compatible with the device 
	//       and app to the given D3DDeviceCombo.
	//--------------------------------------------------------------------------------------
	void EnumD3D9::BuildDepthStencilFormatList( EnumDeviceSettings9* pDeviceSettings )
	{
		D3DFORMAT depthStencilFmt;
		for( int idsf = 0 ; idsf < (int)m_depthStencilPossibleList.size() ; ++idsf )
		{
			depthStencilFmt = m_depthStencilPossibleList[idsf];
			if (SUCCEEDED(CDevice9::d3d9()->CheckDeviceFormat(pDeviceSettings->m_adapterOrdinal, 
				pDeviceSettings->m_deviceType, pDeviceSettings->m_adapterFormat, 
				D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, depthStencilFmt)))
			{
				if (SUCCEEDED(CDevice9::d3d9()->CheckDepthStencilMatch(pDeviceSettings->m_adapterOrdinal, 
					pDeviceSettings->m_deviceType, pDeviceSettings->m_adapterFormat, 
					pDeviceSettings->m_backbufferFormat, depthStencilFmt)))
				{
					pDeviceSettings->m_depthStencilFormatList.push_back( depthStencilFmt );
				}
			}
		}
	}

	//--------------------------------------------------------------------------------------
	// Adds all multisample types that are compatible with the device and app to
	//       the given D3DDeviceCombo.
	//--------------------------------------------------------------------------------------
	void EnumD3D9::BuildMultiSampleTypeList( EnumDeviceSettings9* pDeviceSettings )
	{
		D3DMULTISAMPLE_TYPE msType;
		DWORD msQuality;
		for( int imst = 0 ; imst < (int)m_multiSampleTypeList.size() ; ++imst )
		{
			msType = m_multiSampleTypeList[imst];
			if( SUCCEEDED( CDevice9::d3d9()->CheckDeviceMultiSampleType( pDeviceSettings->m_adapterOrdinal, 
				pDeviceSettings->m_deviceType, pDeviceSettings->m_backbufferFormat, 
				pDeviceSettings->m_windowed, msType, &msQuality ) ) )
			{
				pDeviceSettings->m_multiSampleTypeList.push_back( msType );
				if( msQuality > m_multisampleQualityMax + 1 )
					msQuality = m_multisampleQualityMax + 1;
				pDeviceSettings->m_multiSampleQualityList.push_back( msQuality );
			}
		}
	}

	//--------------------------------------------------------------------------------------
	// Find any conflicts between the available depth/stencil formats and
	//       multisample types.
	//--------------------------------------------------------------------------------------
	void EnumD3D9::BuildDSMSConflictList( EnumDeviceSettings9* pDeviceSettings )
	{
		EnumDSMSConflict9 DSMSConflict;

		for( int iDS = 0 ; iDS < (int)pDeviceSettings->m_depthStencilFormatList.size() ; ++iDS )
		{
			D3DFORMAT dsFmt = pDeviceSettings->m_depthStencilFormatList[iDS];

			for( int iMS = 0 ; iMS < (int)pDeviceSettings->m_multiSampleTypeList.size() ; ++iMS )
			{
				D3DMULTISAMPLE_TYPE msType = pDeviceSettings->m_multiSampleTypeList[iMS];

				if( FAILED( CDevice9::d3d9()->CheckDeviceMultiSampleType( pDeviceSettings->m_adapterOrdinal, pDeviceSettings->m_deviceType,
					dsFmt, pDeviceSettings->m_windowed, msType, NULL ) ) )
				{
					DSMSConflict.dsFormat = dsFmt;
					DSMSConflict.msType = msType;
					pDeviceSettings->m_dsmsConflictList.push_back( DSMSConflict );
				}
			}
		}
	}


	//--------------------------------------------------------------------------------------
	// Adds all present intervals that are compatible with the device and app 
	//       to the given D3DDeviceCombo.
	//--------------------------------------------------------------------------------------
	void EnumD3D9::BuildPresentIntervalList( EnumDeviceInfo9* pDeviceInfo, 
		EnumDeviceSettings9* pDeviceSettings )
	{
		UINT pi;
		for( int ipi = 0; ipi < (int)m_presentIntervalList.size(); ipi++ )
		{
			pi = m_presentIntervalList[ipi];
			if( pDeviceSettings->m_windowed )
			{
				if( pi == D3DPRESENT_INTERVAL_TWO ||
					pi == D3DPRESENT_INTERVAL_THREE ||
					pi == D3DPRESENT_INTERVAL_FOUR )
				{
					// These intervals are not supported in windowed mode.
					continue;
				}
			}
			// Note that D3DPRESENT_INTERVAL_DEFAULT is zero, so you
			// can't do a caps check for it -- it is always available.
			if( pi == D3DPRESENT_INTERVAL_DEFAULT ||
				(pDeviceInfo->m_caps.PresentationIntervals & pi) )
			{
				pDeviceSettings->m_presentIntervalList.push_back( pi );
			}
		}
	}



	//--------------------------------------------------------------------------------------
	// Release all the allocated EnumAdapterInfo9 objects and empty the list
	//--------------------------------------------------------------------------------------
	void EnumD3D9::ClearAdapterInfoList()
	{
		EnumAdapterInfo9* pAdapterInfo;
		for( int i = 0 ; i < (int)m_adapterInfoList.size() ; ++i )
		{
			pAdapterInfo = m_adapterInfoList[i];
			delete pAdapterInfo;
		}

		m_adapterInfoList.clear();
	}



	//--------------------------------------------------------------------------------------
	// Call GetAdapterInfoList() after Enumerate() to get a STL vector of 
	//       EnumAdapterInfo9* 
	//--------------------------------------------------------------------------------------
	const std::vector<EnumAdapterInfo9*>& EnumD3D9::GetAdapterInfoList()
	{
		return m_adapterInfoList;
	}



	//--------------------------------------------------------------------------------------
	EnumAdapterInfo9* EnumD3D9::GetAdapterInfo( UINT AdapterOrdinal )
	{
		for( int iAdapter = 0 ; iAdapter < (int)m_adapterInfoList.size(); ++iAdapter )
		{
			EnumAdapterInfo9* pAdapterInfo = m_adapterInfoList[iAdapter];
			if( pAdapterInfo->m_adapterOrdinal == AdapterOrdinal )
				return pAdapterInfo;
		}

		return NULL;
	}


	//--------------------------------------------------------------------------------------
	EnumDeviceInfo9* EnumD3D9::GetDeviceInfo( UINT AdapterOrdinal, D3DDEVTYPE DeviceType )
	{
		EnumAdapterInfo9* pAdapterInfo = GetAdapterInfo( AdapterOrdinal );
		if( pAdapterInfo )
		{
			for( int iDeviceInfo = 0 ; iDeviceInfo < (int)pAdapterInfo->m_deviceInfoList.size() ; ++iDeviceInfo )
			{
				EnumDeviceInfo9* pDeviceInfo = pAdapterInfo->m_deviceInfoList[iDeviceInfo];
				if( pDeviceInfo->m_deviceType == DeviceType )
					return pDeviceInfo;
			}
		}

		return NULL;
	}


	//--------------------------------------------------------------------------------------
	// 
	//--------------------------------------------------------------------------------------
	EnumDeviceSettings9* EnumD3D9::GetDeviceSettingsCombo( UINT AdapterOrdinal, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed )
	{
		EnumDeviceInfo9* pDeviceInfo = GetDeviceInfo( AdapterOrdinal, DeviceType );
		if( pDeviceInfo )
		{
			for( int iDeviceCombo = 0; iDeviceCombo < (int)pDeviceInfo->m_deviceSettingsList.size() ; ++iDeviceCombo )
			{
				EnumDeviceSettings9* pDeviceSettings = pDeviceInfo->m_deviceSettingsList[iDeviceCombo];
				if( pDeviceSettings->m_adapterFormat == AdapterFormat &&
					pDeviceSettings->m_backbufferFormat == BackBufferFormat &&
					pDeviceSettings->m_windowed == bWindowed )
					return pDeviceSettings;
			}
		}

		return NULL;
	}
	//--------------------------------------------------------------------------------------
	EnumAdapterInfo9::~EnumAdapterInfo9( void )
	{
		EnumDeviceInfo9* pDeviceInfo;
		for( int i = 0 ; i < (int)m_deviceInfoList.size() ; ++i )
		{
			pDeviceInfo = m_deviceInfoList[i];
			delete pDeviceInfo;
		}
		m_deviceInfoList.clear();
	}

	//--------------------------------------------------------------------------------------
	EnumDeviceInfo9::~EnumDeviceInfo9( void )
	{
		EnumDeviceSettings9* pDeviceSettings;
		for( int i = 0 ; i < (int)m_deviceSettingsList.size() ; ++i )
		{
			pDeviceSettings = m_deviceSettingsList[i];
			delete pDeviceSettings;
		}
		m_deviceSettingsList.clear();
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::ResetPossibleDepthStencilFormats()
	{
		m_depthStencilPossibleList.clear();
		m_depthStencilPossibleList.push_back( D3DFMT_D16 );
		m_depthStencilPossibleList.push_back( D3DFMT_D15S1 );
		m_depthStencilPossibleList.push_back( D3DFMT_D24X8 );
		m_depthStencilPossibleList.push_back( D3DFMT_D24S8 );
		m_depthStencilPossibleList.push_back( D3DFMT_D24X4S4 );
		m_depthStencilPossibleList.push_back( D3DFMT_D32 );
	}


	//--------------------------------------------------------------------------------------
	const std::vector<D3DFORMAT>& EnumD3D9::GetPossibleDepthStencilFormatList() 
	{
		return m_depthStencilPossibleList;
	}


	//--------------------------------------------------------------------------------------
	const std::vector<D3DMULTISAMPLE_TYPE>& EnumD3D9::GetPossibleMultisampleTypeList()
	{
		return m_multiSampleTypeList;
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::ResetPossibleMultisampleTypeList()
	{
		m_multiSampleTypeList.clear();
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_NONE );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_NONMASKABLE );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_2_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_3_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_4_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_5_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_6_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_7_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_8_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_9_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_10_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_11_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_12_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_13_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_14_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_15_SAMPLES );
		m_multiSampleTypeList.push_back( D3DMULTISAMPLE_16_SAMPLES );
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::GetPossibleVertexProcessingList( bool* pbSoftwareVP, bool* pbHardwareVP, bool* pbPureHarewareVP, bool* pbMixedVP )
	{
		*pbSoftwareVP = m_softwareVP;
		*pbHardwareVP = m_hardwareVP;
		*pbPureHarewareVP = m_pureHarewareVP;
		*pbMixedVP = m_mixedVP;
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::SetPossibleVertexProcessingList( bool bSoftwareVP, bool bHardwareVP, bool bPureHarewareVP, bool bMixedVP )
	{
		m_softwareVP = bSoftwareVP;
		m_hardwareVP = bHardwareVP;
		m_pureHarewareVP = bPureHarewareVP;
		m_mixedVP = bMixedVP;
	}


	//--------------------------------------------------------------------------------------
	const std::vector<UINT>& EnumD3D9::GetPossiblePresentIntervalList()
	{
		return m_presentIntervalList;
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::ResetPossiblePresentIntervalList()
	{
		m_presentIntervalList.clear();
		m_presentIntervalList.push_back( D3DPRESENT_INTERVAL_IMMEDIATE );
		m_presentIntervalList.push_back( D3DPRESENT_INTERVAL_DEFAULT );
		m_presentIntervalList.push_back( D3DPRESENT_INTERVAL_ONE );
		m_presentIntervalList.push_back( D3DPRESENT_INTERVAL_TWO );
		m_presentIntervalList.push_back( D3DPRESENT_INTERVAL_THREE );
		m_presentIntervalList.push_back( D3DPRESENT_INTERVAL_FOUR );
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::SetResolutionMinMax( UINT nMinWidth, UINT nMinHeight, 
		UINT nMaxWidth, UINT nMaxHeight )
	{
		m_minWidth = nMinWidth;
		m_minHeight = nMinHeight;
		m_maxWidth = nMaxWidth;
		m_maxHeight = nMaxHeight;
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::SetRefreshMinMax( UINT nMin, UINT nMax )
	{
		m_refreshMin = nMin;
		m_refreshMax = nMax;
	}


	//--------------------------------------------------------------------------------------
	void EnumD3D9::SetMultisampleQualityMax( UINT nMax )
	{
		if( nMax > 0xFFFF )
			nMax = 0xFFFF;
		m_multisampleQualityMax = nMax;
	}

} // namespace xeres
